/*
 * GIPF; Team 2, Maastricht University; Project 2-1, Fall 2012.
 */
package gipf.trunk;

import gipf.trunk.Pieces.Piece;

import java.util.ArrayList;

/**
 * 
 * @author log:
 */
public class Pieces {

	private ArrayList<Piece> pieces;
	public ArrayList<Piece> lastMoves = new ArrayList<Piece>();

	// contains a list of pieces

	public Pieces() {
		pieces = new ArrayList<Piece>();

	}

	// initialized with an empty list of pieces
	// which are created on the happening of an
	// event so when putting a new piece on the board

	public ArrayList<Piece> getPiecelist() {
		return pieces;
	}

	// method for inserting a piece into the board
	// next specifies the node you WANT to place the piece
	// NOT the node where the piece came from, because
	// those don't exist.

	public void insertPiece(NonMovable.Node next, int player, int direction) {

		int[] coordinate = next.getCoordinate();

		for (int index = 0; index < pieces.size(); index++) {
			if (pieces.get(index).getCoordinate()[0] == coordinate[0]
					&& pieces.get(index).getCoordinate()[1] == coordinate[1]) {
				move(pieces.get(index), direction);
				createPiece(player, next);
			}
		}
		createPiece(player, next);
	}

	// method for checking if a line is full
	// next specifies the node you WANT to place the piece on
	// NOT the node the piece would come from.
	// direction specifies the direction that
	// would already have been performed to reach the node 'next'
	public void removeLast() {
		pieces.remove(pieces.size() - 1);
	}
        
        public ArrayList<Piece> getLast(){
            return lastMoves;
        }

	public boolean fullLine(NonMovable.Node start, int direction) {
		Piece startPiece = onNode(start);
		ArrayList<Piece> aligned = alignedPieces(onNode(start), direction);

		ArrayList<Integer> indexList = lineIndexList(startPiece, direction);

		if (aligned.size() == indexList.size())
			return true;

		return false;

	}

	public Piece onNode(NonMovable.Node node) {
		for (int i = 0; i < pieces.size(); i++) {
			if (pieces.get(i).onANode(node))
				return pieces.get(i);
		}
		return null;
	}

	// method for creation of pieces.
	// they get created by a number for the player
	// and a node describing their current position

	public ArrayList<Integer> wholeLineIndexList(Piece start, int direction) {
		ArrayList<Integer> indexList = new ArrayList<Integer>();
		Integer startInt = new Integer(start.getCurrent().index());
		indexList.add(startInt);
		NonMovable.Node next1 = start.currentNode.getAdjacent(direction);
		NonMovable.Node next2 = start.currentNode
				.getAdjacent((direction + 3) % 6);

		try {
			while (!next1.getAdjacent(direction).equals(null)) {
				indexList.add(next1.index());
				next1 = next1.getAdjacent(direction);
			}
		} catch (Exception e) {

		}

		ArrayList<Integer> tempList = new ArrayList<Integer>();
		for (int z = 0; z < indexList.size(); z++) {
			tempList.add(indexList.get(indexList.size() - 1 - z));
		}
		indexList = tempList;

		try {
			while (!next2.getAdjacent((direction + 3) % 6).equals(null)) {
				indexList.add(next2.index());
				next2 = next2.getAdjacent((direction + 3) % 6);
			}
		} catch (Exception e2) {

		}
		// System.out.println(indexList);
		return indexList;

	}

	public ArrayList<Integer> lineIndexList(Piece start, int direction) {
		ArrayList<Integer> indexList = new ArrayList<Integer>();
		Integer startInt = new Integer(start.getCurrent().index());
		indexList.add(startInt);
		NonMovable.Node next = start.currentNode.getAdjacent(direction);
		while (next.getAdjacent(direction) != null) {
			indexList.add(next.index());
			next = next.getAdjacent(direction);
		}
		return indexList;

	}

	public ArrayList<Piece> alignedPieces(Piece start, int direction) {
		ArrayList<Piece> aligned = new ArrayList<Piece>();
		ArrayList<Integer> indexList = lineIndexList(start, direction);

		for (int index = 0; index < pieces.size(); index++) {
			for (int count = 0; count < indexList.size(); count++) {
				if (pieces.get(index).getCurrent().index() == indexList
						.get(count)) {
					aligned.add(pieces.get(index));
					if (aligned.size() == indexList.size())
						break;
				}
			}
		}

		return aligned;

	}

	public void createPiece(int player, NonMovable.Node current) {
		Piece newPiece = new Piece();
		newPiece.setCurrent(current);
		newPiece.setPlayer(player);
		pieces.add(newPiece);
	}

	// method for moving a piece into a certain direction
	// recursive
	// can only be executed if it was checked before if the line is not full!

	public void move(Piece piece, int direction) {
		ArrayList<Piece> sameLine = pieces;// inLineWith(piece, direction);
		lastMoves.add(piece);

		NonMovable.Node next = piece.getCurrent().getAdjacent(direction);

		for (int index = 0; index < sameLine.size(); index++) {
			NonMovable.Node actual = sameLine.get(index).getCurrent();
			if (next == actual) {
				move(sameLine.get(index), direction);
			}

		}

		piece.setCurrent(next);

	}

	public ArrayList<Piece> getPlayerPieces(int player) {
		ArrayList<Piece> tempList = new ArrayList<Piece>();

		for (int i = 0; i < pieces.size(); i++) {
			if (pieces.get(i).getPlayer() == player)
				tempList.add(pieces.get(i));
		}

		return tempList;

	}

	public void resetLastMoves() {
		lastMoves.clear();
	}

	public ArrayList<ArrayList<Piece>> finishedLines() {
		ArrayList<ArrayList<Piece>> finishedLines = new ArrayList<ArrayList<Piece>>();

		for (int i = 0; i < lastMoves.size(); i++) {
			Piece current = lastMoves.get(i);
			// System.out.println(current);
			for (int direction = 0; direction < 3; direction++) {
				ArrayList<Integer> currentLine = wholeLineIndexList(current,
						direction);
				// System.out.println(currentLine);

				ArrayList<Piece> toBeUsed = onLine(currentLine);

				/*
				 * for (int z = 0; z < toBeUsed.size(); z++) { Piece other =
				 * toBeUsed.get(z);
				 * 
				 * for (int index = 0; index < currentLine.size(); index++) { if
				 * (other.getCurrent().index() == currentLine .get(index)) {
				 * tempList.add(other); System.out.println(other + "\n"); }
				 * 
				 * } }
				 */
				toBeUsed = removeDistant(toBeUsed, current, direction);
				//System.out.println(toBeUsed);

				if (toBeUsed.size() > 3 && !finishedLines.contains(toBeUsed))
					finishedLines.add(toBeUsed);
			}
		}
		return finishedLines;

	}

	private ArrayList<Piece> onLine(ArrayList<Integer> currentLine) {
		ArrayList<Piece> onLine = new ArrayList<Piece>();
		int index;

		for (int z = 0; z < currentLine.size(); z++) {
			index = currentLine.get(z);
			for (int i = 0; i < pieces.size(); i++) {
				Piece current = pieces.get(i);

				if (current.getCurrent().index() == index)
					onLine.add(current);
			}

		}

		// System.out.println(onLine);
		return onLine;
	}

	private ArrayList<Piece> removeDistant(ArrayList<Piece> tempList,
			Piece current, int direction) {

		for (int i = 0; i < tempList.size(); i++) {
			System.out.println(tempList.get(i) + " " + current.reachable(tempList.get(i), direction));
			if (!current.reachable(tempList.get(i), direction))
				tempList.remove(i);
		}
		return tempList;
	}

	// method which for a given piece
	// (maybe one that was moved during the last turn)
	// checks into all directions if it created a line of four
	// the result of this method is an arraylist
	// which at .get(0) contains either null
	// or the line that this piece is in, given the 0/3 direction
	// same for .get(1 or 2) with 1/4 or 2/5 direction

	public ArrayList<ArrayList<Piece>> fourInALine(Piece start) {
		ArrayList<ArrayList<Piece>> finishedLines = new ArrayList<ArrayList<Piece>>();
		for (int i = 0; i < 3; i++) {
			finishedLines.add(fourAdjacent(start, i));
		}
		return finishedLines;
	}

	// method for returning all pieces of one certain
	// player, which are adjacent given a certain direction
	// this might also return lines of 1/2/3, but is returning null
	// if the line is shorter than 4 atm. (for evaluating???)

	public ArrayList<Piece> fourAdjacent(Piece start, int direction) {
		ArrayList<Piece> piecesInLine = inLineWithPlayer(start, direction);
		ArrayList<Piece> adjacent = new ArrayList<Piece>();

		adjacent.add(start);
		int counter = 0;
		int piecesInRow = piecesInLine.size();

		while (adjacent.size() < 4 && counter < piecesInRow) {
			for (int z = 0; z < adjacent.size(); z++) {
				for (int i = 0; i < piecesInLine.size(); i++) {
					if (adjacent.get(z).getCurrent().getAdjacent(direction) == piecesInLine
							.get(i).getCurrent()
							|| adjacent.get(z).getCurrent()
									.getAdjacent(direction + 3) == piecesInLine
									.get(i).getCurrent()
							&& !adjacent.contains(piecesInLine.get(i))) {
						adjacent.add(piecesInLine.get(i));
					}
				}
			}
			counter++;
		}

		if (adjacent.size() < 4)
			return null;
		else
			return adjacent;

	}

	// method for checking through the list of
	// pieces in order to find pieces which can confirm
	// to be on a line with one certain piece, given
	// a certain direction. It not only looks for adjacent
	// pieces, but for any pieces which are on the same line as
	// the given starting piece.

	public ArrayList<Piece> inLineWith(Piece piece, int direction) {
		ArrayList<Piece> piecesInLine = new ArrayList<Piece>();

		for (int index = 0; index < pieces.size(); index++) {
			if (pieces.get(index).onLine(piece, getVector(direction)))
				piecesInLine.add(pieces.get(index));
		}
		return piecesInLine;
	}

	// method for checking through the list of
	// pieces in order to find pieces which can confirm
	// to be on a line with one certain piece, given
	// a certain direction. It not only looks for adjacent
	// pieces, but for any pieces which are on the same line as
	// the given starting piece.
	// they are only of the same kind as the current player

	public ArrayList<Piece> inLineWithPlayer(Piece piece, int direction) {
		ArrayList<Piece> piecesInLine = new ArrayList<Piece>();

		for (int index = 0; index < pieces.size(); index++) {
			if (pieces.get(index).getPlayer() == piece.getPlayer())
				if (pieces.get(index).onLine(piece, getVector(direction)))
					piecesInLine.add(pieces.get(index));
		}
		return piecesInLine;
	}

	// a method translating any direction into
	// a corresponding vector which describes
	// the change in x/y positions
	// which exactely are for moving N or S (to adj. 0 or 3) = 0,1
	// for moving NE/SW (to adj. 1 or 4) = 1,1
	// for moving SE/NW (to adj. 2 or 5) = 1,0
	// each piece can then in the method above check if it is
	// on a line described by the vector in either a positive or
	// a negative direction

	public int[] getVector(int direction) {
		int[] vector = new int[2];
		switch (direction % 3) {
		case 0:
			vector[0] = 0;
			vector[1] = 1;
			break;
		case 1:
			vector[0] = 1;
			vector[1] = 1;
			break;
		case 2:
			vector[0] = 1;
			vector[1] = 0;
			break;
		}
		return vector;
	}

	// a string representation of the piecelist
	// showing all it's content.
	public String toString() {
		String toReturn = "";
		for (int index = 0; index < pieces.size(); index++) {
			toReturn += pieces.get(index) + "\n";
		}
		return toReturn;
	}

	class Piece extends Pieces {

		// each piece has a reference to
		// the node it is placed on
		// as well as an integer indicating
		// which player owns this tile

		private NonMovable.Node currentNode;
		private int player;

		// initializing an empty piece
		// it is filled by the PieceList

		public Piece() {
			// isnt it better to create the piece object already
			// with all of his properties?
		}

		public int adjacent(Piece other) {
			return currentNode.adjacent(other.getCurrent());
		}

		// returns the current position of
		// the piece via the node it's placed on

		public String getPosition() {
			return currentNode.getPosition(); // GUI position or Board position?
												// if a relative board position
												// is kept, GUI only takes the
												// values and scales the
												// positions
		}

		// returns the node the piece is placed on
		// !!!better non letting anyone except board access the nodes, only
		// referenced by a piece and if asked by pieceslist

		public NonMovable.Node getCurrent() {
			return currentNode;
		}

		// setter for the current node
		// used for initializing & movement

		public void setCurrent(NonMovable.Node current) {
			currentNode = current;
		}

		// returns the current coordinate of the
		// piece vie the node it's placed on

		public int[] getCoordinate() {
			return currentNode.getCoordinate();
		}

		// returns the integer value representing
		// the player which owns the piece

		public int getPlayer() {
			return player;
		}

		// setter for the player field,
		// used in initialization

		public void setPlayer(int player) {
			this.player = player;
		}

		// boolean for checking if
		// this piece is on a line with another piece
		// given a certain direction

		public boolean onLine(Piece start, int[] vector) {
			for (int i = 1; i < 7; i++) {
				if (start.getCoordinate()[0] == getCoordinate()[0]
						+ (vector[0] * i)
						&& start.getCoordinate()[1] == getCoordinate()[1]
								+ (vector[1] * i))
					return true;
				if (start.getCoordinate()[0] == getCoordinate()[0]
						- (vector[0] * i)
						&& start.getCoordinate()[1] == getCoordinate()[1]
								- (vector[1] * i))
					return true;
			}
			return false;
		}

		public boolean reachable(Piece goal, int direction) {

			Piece next1 = onNode(getCurrent().getAdjacent(direction));
			Piece next2 = onNode(getCurrent().getAdjacent((direction + 3) % 6));

			if (goal == this)
				return true;
			
			try {
				while (next1 != null) {
					if (next1 == goal)
						return true;
					next1 = onNode(next1.getCurrent().getAdjacent(direction));
				}
			} catch (Exception e) {
			}
			try {
				while (next2 != null) {
					if (next2 == goal)
						return true;
					next2 = onNode(next2.getCurrent().getAdjacent(
							(direction + 3) % 6));
				}
			} catch (Exception e) {

			}
			return false;
		}

		// toString method for printing,
		// used for testing

		public boolean onANode(NonMovable.Node node) {
			return this.getCurrent() == node;
		}

		public String toString() {
			return getPosition() + " Taken by Player_" + player;
		}

	}

}
